import java.net.*;
import java.util.Scanner;
import java.io.*;


public class ExperimentServer {
	
	private static PrintStream fileOut;
	
	public static void main(String[] args) throws FileNotFoundException {
		if (args.length != 2) {
			printUsage();
			System.exit(-1);
		}
		
		int port = Integer.parseInt(args[0]);
		int stateId = Integer.parseInt(args[1]);
		String prefix = "Server(State=" + stateId + ")";
		Scanner stdin = new Scanner(System.in);
		fileOut = new PrintStream(new File(prefix + ".txt"));
		
			
		ServerSocket serverSocket;
		try {
			serverSocket = new ServerSocket(port);
		} catch (Exception e1) {
			printlnToFileAndStdOut(prefix + " ServerSocket constructor threw an exception");
			e1.printStackTrace();
			e1.printStackTrace(fileOut);			
			stdin.close();
			return;
		}
		
		printlnToFileAndStdOut(prefix + " Created ServerSocket at port " + port);
		
		while (true) {
			Socket clientSocket;
			try {
				clientSocket = serverSocket.accept();
			} catch (IOException e2) {
				printlnToFileAndStdOut(prefix + " clientSocket.accept() threw an exception");
				e2.printStackTrace();
				e2.printStackTrace(fileOut);
				break;
			}
			//PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
			
			BufferedWriter out;
			BufferedReader in;
			try {
				out = new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream()));
				in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
			} catch (IOException e2) {
				printlnToFileAndStdOut(prefix + " PrintWriter/BufferedReader constructors threw an exception");
				e2.printStackTrace();
				e2.printStackTrace(fileOut);
				break;
			}
			
		    String inputLine = null, outputLine = null;
		    
		    // get client test payload
		    try {
				inputLine = in.readLine();
				if (inputLine == null)
	            {
	                printlnToFileAndStdOut(prefix + " BufferedReader.readLine() returns null when trying to read" +
	                		"test payload from client.");
	            } else {
	            	printlnToFileAndStdOut(prefix + " Received Client Test Payload With Content: " + inputLine);
	            }
			} catch (IOException e1) {
				printlnToFileAndStdOut(prefix + " BufferedReader.readLine() threw an exception");
				e1.printStackTrace();
				e1.printStackTrace(fileOut);
			}	
		    
		    // send server test payload
		    outputLine = "I am a server instance set to stateId " + stateId;			    
			try {
				out.write(outputLine + "\n");
				out.flush();
			} catch (IOException e) {
				printlnToFileAndStdOut(prefix + " BufferedWriter threw an exception. " + 
						"Preparing to wait for new incoming clients...");
				break;
			}
		    
			String stdinput = null;
		    switch (stateId) {		    	
		    	case 0:
					try {
						while ((inputLine = in.readLine()) != null) {
			    			printlnToFileAndStdOut(prefix + " Received Client Payload With Content: " + inputLine);
			    		}
						printlnToFileAndStdOut(prefix + " BufferedReader.readLine() returns null when trying to read " +
		                		"action payload from client. Closing socket and preparing for new connection...");
					} catch (IOException e1) {
						printlnToFileAndStdOut(prefix + " BufferedReader.readLine() threw an exception");
						e1.printStackTrace();
						e1.printStackTrace(fileOut);
					}
					
		    		break;
		    	case 1:
		    		while (true) {
		    			printlnToFileAndStdOut(prefix + " sending payload to client");
		    			outputLine = prefix + " Dummy Payload From Server";
		    			try {
		    				out.write(outputLine + "\n");
		    				out.flush();
			    			Thread.sleep(200);
		    			} catch (IOException e) {
		    				printlnToFileAndStdOut(prefix + " BufferedWriter threw an exception. " + 
		    						"Preparing to wait for new incoming clients...");
		    				e.printStackTrace();
		    				e.printStackTrace(fileOut);
		    				break;
		    			} catch (InterruptedException e) {
		    				printlnToFileAndStdOut(prefix + " Received Interrupted Exception.");
		    				e.printStackTrace();
							e.printStackTrace(fileOut);
						}
		    		}
		    		break;
		    	case 2:
		    		printlnToFileAndStdOut(prefix + " Closing the socket.");
		    		
					try {
						clientSocket.close();
					} catch (IOException e) {
						e.printStackTrace();
						e.printStackTrace(fileOut);
					}
					
		    		printlnToFileAndStdOut(prefix + " Done closing the socket.");
		    		
		    		fileOut.print(prefix + " When ready to accept a new connection, " +
		    				"enter anything in the console and hit return: ");
		    		System.out.print(prefix + " When ready to accept a new connection, " +
		    				"enter anything in the console and hit return: ");
		    		// simply block the process until use entered something
		    		stdinput = stdin.nextLine();
		    		printlnToFileAndStdOut(stdinput);
		    		break;
		    	case 3:
		    		printlnToFileAndStdOut(prefix + " Shutting the socket's input stream.");
					try {
						clientSocket.shutdownInput();
					} catch (IOException e) {
						e.printStackTrace();
						e.printStackTrace(fileOut);
					}
		    		
		    		printlnToFileAndStdOut(prefix + " Done shutting the socket's input stream");
		    		
		    		fileOut.print(prefix + " When ready to accept a new connection, " +
		    				"enter anything in the console and hit return: ");
		    		System.out.print(prefix + " When ready to accept a new connection, " +
		    				"enter anything in the console and hit return: ");
		    		// simply block the process until use entered something
		    		stdinput = stdin.nextLine();
		    		printlnToFileAndStdOut(stdinput);
		    		break;
		    	case 4:
		    		printlnToFileAndStdOut(prefix + " Shutting the socket's output stream.");
					try {
						clientSocket.shutdownOutput();
					} catch (IOException e) {
						e.printStackTrace();
						e.printStackTrace(fileOut);
					}
		    		
		    		printlnToFileAndStdOut(prefix + " Done shutting the socket's output stream");
		    		
		    		fileOut.print(prefix + " When ready to accept a new connection, " +
		    				"enter anything in the console and hit return: ");
		    		System.out.print(prefix + " When ready to accept a new connection, " +
		    				"enter anything in the console and hit return: ");
		    		// simply block the process until use entered something
		    		stdinput = stdin.nextLine();
		    		printlnToFileAndStdOut(stdinput);
		    		break;
		    	case 5:
		    		printlnToFileAndStdOut(prefix + " Shutting the socket's input and output stream.");
					try {
						clientSocket.shutdownInput();
						clientSocket.shutdownOutput();
					} catch (IOException e) {
						e.printStackTrace();
						e.printStackTrace(fileOut);
					}
		    		
		    		printlnToFileAndStdOut(prefix + " done shutting the socket's input and output stream");
		    		
		    		fileOut.print(prefix + " When ready to accept a new connection, " +
		    				"enter anything in the console and hit return: ");
		    		System.out.print(prefix + " When ready to accept a new connection, " +
		    				"enter anything in the console and hit return: ");
		    		// simply block the process until use entered something
		    		stdinput = stdin.nextLine();
		    		printlnToFileAndStdOut(stdinput);		    		
		    		break;
		    	case 6:
		    		printlnToFileAndStdOut("In crash state. Will sleep from now on. When ready to creash, " +
		    				"Please exit this program through some violent means");
		    		// simply block the process
		    		while(true) {
			    		try {
							Thread.sleep(1000);
						} catch (InterruptedException e) {
							printlnToFileAndStdOut(prefix + " Received Interrupted Exception.");
							e.printStackTrace();
							e.printStackTrace(fileOut);
							continue;
						}
		    		}
		    	case 7:
		    		printlnToFileAndStdOut("In networkDown state. Will sleep from now on. When ready to bring down the network, " +
		    				"Please bring down the network of the server through some violent means");
		    		// simply block the process
		    		while(true) {
			    		try {
							Thread.sleep(1000);
						} catch (InterruptedException e) {
							printlnToFileAndStdOut(prefix + " Received Interrupted Exception.");
							e.printStackTrace();
							e.printStackTrace(fileOut);
							continue;
						}
		    		}
		    	case 9:
		    		while (true) {
		    			boolean isClosed = clientSocket.isClosed();
		    			printlnToFileAndStdOut(prefix + " clientSocket.isClosed() = " + isClosed); 
		    			
				    	try {
							Thread.sleep(100);
						} catch (InterruptedException e) {
							printlnToFileAndStdOut(prefix + " Received Interrupted Exception.");
							e.printStackTrace();
							e.printStackTrace(fileOut);
							continue;
						}
		    		}
		    	default:
		    		printlnToFileAndStdOut(prefix + " unimplemented stateId");
		    		break;
		    }
		    
		    printlnToFileAndStdOut(prefix + " done with this socket. Closing socket and preparing for new connection...");
		    try {
				clientSocket.close();
			} catch (IOException e) {
				printlnToFileAndStdOut(prefix + " clientSocket.close() threw exception");
				e.printStackTrace();
				e.printStackTrace(fileOut);
			}			    

		    printlnToFileAndStdOut("");
		}
		
	    try {
			serverSocket.close();
		} catch (IOException e) {
			printlnToFileAndStdOut(prefix + " serverSocket.close() threw exception");
			e.printStackTrace();
			e.printStackTrace(fileOut);
		}
	    
	    stdin.close();
	    fileOut.close();
	}
	
	private static void printUsage() {
		System.out.println("Usage: java ExperimentServer [port-number] [stateId]");
	}
	
	private static void printlnToFileAndStdOut(String str) {
		System.out.println(str);
		if (fileOut != null) {
			fileOut.println(str);
		}
	}
}
